
template<int n>
bool Cell<n>::isCell()
{
    return(true);
}

template<int n>
inline State<n> Cell<n>::getBarycenter()
{
    return(data->barycenter(indices));
}

template<int n>
inline QList<double> Cell<n>::getRandomParams()
{
    return(data->randomParams(indices));
}

template<int n>
inline int Cell<n>::getLevel()
{
    return(level);
}

template<int n>
inline int Cell<n>::getVisits()
{
    return(nVisits);
}

template<int n>
inline void Cell<n>::incrementVisits()
{
    nVisits++;
}

template<int n>
inline void Cell<n>::setNode(Node *a_node)
{
    node = a_node;
    id = node->insert(this);
}

template<int n>
AV Cell<n>::getVals(QList<double> list)
{
    QList<double> cons;
    for(int j = 0 ; j < data->states[indices[0]].cons.size() ; j++ )
        cons << 0;
    AV result(0 , cons );

    QList<double> l;

    for(int i=0; i < indices.size(); i++)
    {

        double temp = 1;
        for(int j = 0 ; j < list.size() ; j++)
            temp *= (1 - fabs(data->states[indices[i]].params[j]-list[j]));
        l << temp;
    }

    for(int i=0; i < indices.size(); i++)
    {
        for(int j = 0 ; j < data->states[indices[i]].cons.size() ; j++ )
            result.cons[j] += l[i]*data->states[indices[i]].cons[j];
        result.V += (l[i]*data->states[indices[i]].V);
    }

    return(result);
}

template<int n>
void Cell<n>::setVals(double valV , QList<double> valCons, QList<double> list)
{

    QList<double> l , w;
    for(int i=0; i < indices.size(); i++){
        double temp = 1;
        for(int j = 0 ; j < list.size() ; j++){
            temp *= (1 - fabs(data->states[indices[i]].params[j]-list[j]));
        }
        l << temp;
    }
    for(int i=0; i < indices.size(); i++)
    {
        double valVi = valV*l[i];
        if(valVi > data->maxV)
            data->maxV = valVi;

        double coeff = 1;
        if(data->maxV != 0)
            coeff = valVi/(data->maxV);

        double beta = (data->beta*l[i]*coeff*coeff);
        double alpha = (data->alpha*l[i]*coeff);

        for(int j = data->states[indices[i]].cons.size() ; j < valCons.size() ; j++ )
            data->states[indices[i]].cons << 0;
        if(data->states[indices[i]].V < valVi)
            for(int j = 0 ; j < valCons.size() ; j++ )
                data->states[indices[i]].cons[j] = (1-alpha)*data->states[indices[i]].cons[j] + alpha*valCons[j];
        data->states[indices[i]].V = (1-beta)*data->states[indices[i]].V + (beta*valVi);
    }
    data->visit(this);
}

template<int n>
void Cell<n>::subdivide()
{
    Node *newNode = new Node;
    node->replace(id, newNode);

    //First, we sort the indices to put them on an hypercube
    QList<int> sortedIndices;
    int sizeI = indices.size();
    State<n> barycenter = getBarycenter();

    for(int i=0; i < sizeI; i++)
        sortedIndices << 0;

    for(int i=0; i < sizeI; i++)
    {
        int id = 0;
        State<n> s = data->states[indices[i]];

        for(int j=0; j<n; j++)
            if(s.params[j] > barycenter.params[j])
                id += (1<<j);

        sortedIndices[id] = indices[i];
    }

    //We create new vertices, and determine the
    //lists of the vertices indices for each new cell
    QList<QList<int> > list;


    for(int i=0; i < sizeI; i++)
    {
        QList<int> temp;
        temp.append(sortedIndices[i]);
        list.append(temp);
    }

    for(int i=0; i < sizeI; i++)
    {
        //We determine the zeroes in the bits of i
        QList<int> zeroBits;
        for(int j=0; j<n; j++)
            if((i & (1<<j)) == 0)
                zeroBits << j ;

        //Each part of zeroBits of size > 1 corresponds to an edge/face/cube
        int sizeZ = zeroBits.size();
        int jMax = 1 << sizeZ;
        for(int j = 1; j<jMax; j++) //A value of j determines the points that will be in the edge/face/cube
        {
            QList<int> temp;        //List of the indices of those points

            //First we determine which zero bits of i will turn to ones
            QList<int> oneBits;
            for(int k=0; k<sizeZ; k++)
            {
                if((j & (1<<k)) != 0)
                {
                    oneBits.append(zeroBits[k]);
                }
            }

            //Then, for each way we can turn zero, one or more of those bits to one,
            //we get the id of one of the point of the edg/face/cube in the indices list
            int sizeO = oneBits.size();
            int kMax= 1 << sizeO;
            for(int k=0; k<kMax; k++)
            {
                int point = i;
                for(int l=0; l<sizeO; l++)
                {
                    if((k & (1<<l)) != 0)
                    {
                        point += 1 << oneBits[l];
                    }
                }
                temp.append(sortedIndices[point]);
            }

            //We add the barycenter of the points of the edge/face/cube to each of the corresponding points
            int barycenter = data->insert(data->barycenter(temp));
            int sizeT = temp.size();
            for(int k=0; k < sizeT; k++)
            {
                list[sortedIndices.indexOf( temp[k])].append(barycenter);
            }
        }
    }

    //We use theses lists to create the cells, and we
    //add them to the learning data (tree and lookupMap)
    QList<AbstractNode*> under;
    int newLevel = level + 1;

    if(data->maxLevel < newLevel)
    {
        data->maxLevel = newLevel;
    }

    int sizeL = list.size();
    for(int i=0; i< sizeL; i++)
    {
        Cell<n> *cell = new Cell<n>(data, newNode, i, list[i], newLevel);
        under.append(cell);
        data->insert(cell);
    }
    newNode->setUnder(under);

    //Then, we delete the original cell
    data->replace(this, newNode);
}

template<int n>
void Cell<n>::print()
{
    qDebug()<<"Cell:";
    for(int i=0; i<indices.size(); i++)
    {
        QDebug debug = qDebug();
        debug<<indices[i]<<"(";
        for (int j=0; j<n; j++)
        {
            debug <<data->states[indices[i]].params[j]<<",";
        }
        debug <<"cons="<<data->states[indices[i]].cons<<","<<"V="<<data->states[indices[i]].V;
        debug<<")";
    }
}
